use std::{fmt::Debug, ops::Div, path::Path};

use apperror::{AnyError, AnyErrorExt, AnyResult, Report};
use qiniu_func::{download_from_qiniu_cloud, get_upload_token, object_list, upload_to_servive};
use rmpredictdb::model::User;
use serde_json::json;
use tauri::{async_runtime::spawn, path::BaseDirectory, AppHandle, Emitter, Manager};
use tauri_plugin_store::StoreExt;
use tonic_ffi_client::{get_files, List};

use crate::{
    app_error::AppError,
    payload::PayLoad,
    state_manage::{ChildProcessRatatuiState, UserState},
};

fn calculate_size(mut data: f64) -> String {
    let mut filesize = String::new();
    let mut count = 0;
    loop {
        if data.div(1024.0) < 1.0 {
            match count {
                0 => {
                    filesize = format!("{}B", data);
                }
                1 => {
                    filesize = format!("{:.2}KB", data);
                }
                2 => {
                    filesize = format!("{:.2}MB", data);
                }
                _ => {}
            };
            break;
        };
        data = data / 1024.0;
        count = count + 1;
    }
    filesize
}

#[tauri::command]
pub async fn get_user_state(
    _app: tauri::AppHandle,
    state: tauri::State<'_, UserState>,
) -> Result<User, String> {
    state.inner().get_user()
}
#[derive(Debug, serde::Serialize)]
pub struct FileTemp {
    filename: String,
    filesize: String,
}
#[tauri::command]
pub fn get_file_info(path: String) -> Result<FileTemp, String> {
    let path = Path::new(&path);
    let data = std::fs::metadata(path)
        .map_err(|e| {
            log::error!("{}", AnyError::establish(e.into()).report());
            "打开文件出错".to_string()
        })?
        .len() as f64;
    let filesize = calculate_size(data);
    Ok(FileTemp {
        filename: path.file_name().unwrap().to_str().unwrap().into(),
        filesize,
    })
}
//待优化
#[tauri::command]
pub fn login(user: User, app: AppHandle) -> Result<PayLoad<User>, String> {
    login_handler(user, app).map_err(|e| {
        log::error!("{}", e.report());
        "未找到用户信息".into()
    })
}
#[inline]
pub fn login_handler(user: User, app: AppHandle) -> AnyResult<PayLoad<User>> {
    //如果找不着返回AppError
    let user = user.get_instance_from_database()?;

    //持久化存储用户信息
    let store = app.store("user.json").map_err(|e| {
        AnyError::establish(e.into()).report();
        AnyError::builder().message("未找到存储文件").build()
    })?;
    //持久化存储用户信息
    store.set("user", json!(user));
    store.save().map_err(|e| {
        AnyError::establish(e.into()).report();
        AnyError::builder().message("保存用户信息出错").build()
    })?;
    //从资源表中移除store
    store.close_resource();
    Ok(PayLoad::new(1, "查找成功".into(), user))

    //可持久化
}
#[warn(dead_code)]
#[tauri::command]
pub fn register_handler(_user: User) -> Result<(), AppError> {
    unimplemented!();
}

#[tauri::command]
pub fn upload_token(app: AppHandle) -> String {
    let store = app.store("store.bin").unwrap();
    if let Some(token) = store.get("token") {
        return token.as_str().unwrap().to_string();
    };
    let token_provider = get_upload_token();
    let token = token_provider.into_string();
    token
}
#[tauri::command]
pub fn get_object_list(
    username: String,
    file_type: String,
) -> Result<Vec<impl serde::Serialize>, String> {
    let list = object_list(username, file_type)
        .map_err(|e| {
            log::error!("{}", AnyError::establish(e).report());
            "获取文件列表出错".to_string()
        })?
        .into_iter()
        .map(|entry| {
            let filebytes = entry
                .get_entry()
                .as_ref()
                .map(|entry| entry.get_size_as_u64())
                .unwrap();
            let file = Path::new(
                entry
                    .get_entry()
                    .as_ref()
                    .map(|entry| entry.get_key_as_str())
                    .unwrap(),
            );
            let filetype = file.extension().unwrap().to_str().map(|s| s.to_string());
            let filename = file.file_name().unwrap().to_str().map(|s| s.to_string());
            let new_entry = entry
                .filesize(Some(calculate_size(filebytes as f64)))
                .filetype(filetype)
                .filename(filename)
                .entry(None);
            new_entry
        })
        .collect();
    Ok(list)
}
//异步上传
#[tauri::command]
pub async fn async_upload(path: String, username: String, app: AppHandle) {
    spawn(async move {
        match uplaod_file(path, username).await {
            Ok(_) => app.emit("upload", Ok::<&str, &str>("上传成功!")).unwrap(),
            Err(_) => app
                .emit("upload", Err::<&str, &str>("上传失败,请重试!"))
                .unwrap(),
        }
    });
}
#[inline]
async fn uplaod_file(path: String, username: String) -> Result<(), ()> {
    upload_to_servive(path, &username).await.map_err(|e| {
        log::error!("{}", AnyError::establish(e).report());
    })?;
    Ok(())
}
//异步下载
#[tauri::command]
pub async fn download(object_name: String, path: String, app: AppHandle) {
    spawn(async move {
        //事件系统发送结果和错误
        match download_file(object_name, path).await {
            Ok(_) => app.emit("download", "file download success!"),
            Err(_) => app.emit("download", "occur error when download file"),
        }
    });
}
#[inline]
async fn download_file(object_name: String, path: String) -> Result<(), ()> {
    download_from_qiniu_cloud(object_name, path)
        .await
        .map_err(|e| {
            log::error!("{}", AnyError::establish(e).report());
            ()
        })?;
    Ok(())
}
#[tauri::command]
pub async fn get_files_command(username: String) -> Result<Vec<List>, String> {
    let lists = get_files(username)
        .await
        .map_err(|e| {
            let msg = e.to_string();
            log::error!("{}", AnyError::establish(e).report());
            msg
        })?
        .take()
        .into_iter()
        .map(|item| List::from(item))
        .collect();
    Ok(lists)
}

#[tauri::command]
pub async fn ratatui(app: AppHandle) {
    let app_handle = app.clone();
    spawn(async move {
        match ratatui_child_process(&app_handle).await {
            Ok(_) => app_handle.emit("ratatui", Ok::<&str, &str>("processing...")),
            Err(_) => app_handle.emit("ratatui", Ok::<&str, &str>("子进程运行出错")),
        }
    });
}
async fn ratatui_child_process(app: &AppHandle) -> Result<(), ()> {
    let path = app
        .path()
        .resolve("bin/ratatui_vis.exe", BaseDirectory::AppData)
        .map_err(|e| {
            AnyError::establish(e.into()).report();
        })?;
    println!("{:#?}", path);
    #[cfg(target_os = "windows")]
    let child = tokio::process::Command::new("cmd")
        .creation_flags(0x00000010)
        .arg("/c")
        .arg(&path)
        .spawn()
        .map_err(|e| {
            AnyError::establish(e.into()).report();
        })?;
    let state = app.state::<ChildProcessRatatuiState>().inner();
    println!("process:{}", child.id().unwrap());
    state.set_ratatui_process(child);

    Ok(())
}

//test
#[tauri::command]
pub fn test_err() -> Result<(), String> {
    let report = Report::capture(|| -> AnyResult<()> { err_handler() }).backtrace(true);
    log::debug!("{report}");
    Err(report.to_string())
}
fn err_handler() -> AnyResult<()> {
    Err(AnyError::builder()
        .kind(apperror::ErrKind::Unknown)
        .message("这是一个测试")
        .build())
}
